#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <time.h>

#define BUFFER_SIZE         256
#define half_giga           1024*1024*10
#define struct_line_size    sizeof(struct ppmc_line)
#define ppmc_size           half_giga/struct_line_size
#define altas_size          100
#define ctx_max             5
#define PAGESIZE            4096
#define ESCAPE              256
#define MAXIMUM_SCALE       16383
#define ADJUST_BY_AGE       0
#define BARRITA_MAX         100
#define BARRITA_FALTA       '='
#define BARRITA_LISTO       '-'

int addline_counter;

struct ppmc_line {
    char                    model;
    char                    context[5];
    char                    symbol;
    char                    age; //posiblemente no se use, pero por alineacion sobra
                                 //este espacio, as que lo dejo aca definido
    int                     frec;
};

//Variables Globales PPMC
struct ppmc_line *          ppmc_altas;
int                         altas_count;

struct ppmc_line *          ppmc_models;
int                         ppmc_count;

unsigned int                frec[257];

FILE *                      file_to_comp;
int                         ctx_found;
char                        ctx[ ctx_max ];
unsigned short int          exclusion[257];
int                         read_bytes;

unsigned short int          out_symbol;
int                         j;
char                        cur_char_g;
struct ppmc_line *          ppmline_find_res;
struct ppmc_line            ppmline;

int                         ctx_count;
int                         j;
char                        read_buffer[PAGESIZE];
char                        cur_char;

struct ppmc_line *          ppm_find_altas;
struct ppmc_line *          ppm_find_modelos;
int                         ctx_start_altas;
int                         ctx_len_altas;
int                         ctx_start_models;
int                         ctx_len_models;

//Variables Globales Aritmetico
typedef struct {
    unsigned short int      low_count;
    unsigned short int      high_count;
    int                     scale;
    } SYMBOL;

static char                 buffer[ BUFFER_SIZE + 2 ]; /* This is the i/o buffer    */
static char *               current_byte;              /* Pointer to current byte   */

static int                  output_mask;

static int                  input_bytes_left;
static int                  input_bits_left;
static int                  past_eof;
                                       
static unsigned short int   code;
static unsigned short int   low;
static unsigned short int   high;
long                        underflow_bits;

extern long                 underflow_bits;

void initialize_arithmetic_decoder( FILE *stream );
void remove_symbol_from_stream( FILE *stream, SYMBOL *s );
void initialize_arithmetic_encoder( void );
void encode_symbol( FILE *stream, SYMBOL *s );
void flush_arithmetic_encoder( FILE *stream );
short int get_current_count( SYMBOL *s );
short int input_bit( FILE *stream );
void initialize_output_bitstream( void );
void output_bit( FILE *stream, int bit );
void flush_output_bitstream( FILE *stream );
void initialize_input_bitstream( void );
long bit_ftell_output( FILE *stream );
long bit_ftell_input( FILE *stream );


void initialize_output_bitstream()
{
    current_byte = buffer;
    *current_byte = 0;
    output_mask = 0x80;
}

void output_bit( FILE *stream, int bit )
{
    if ( bit )
        *current_byte |= output_mask;
    output_mask >>= 1;
    if ( output_mask == 0 )
    {
        output_mask = 0x80;
        current_byte++;
        if ( current_byte == ( buffer + BUFFER_SIZE ) )
        {
            fwrite( buffer, 1, BUFFER_SIZE, stream );
            current_byte = buffer;
        }
        *current_byte = 0;
    }
}

void flush_output_bitstream( FILE *stream )
{
    fwrite( buffer, 1, (size_t)( current_byte - buffer ) + 1, stream );
    current_byte = buffer;
}

void initialize_input_bitstream()
{
    input_bits_left = 0;
    input_bytes_left = 1;
    past_eof = 0;
}

short int input_bit( FILE *stream )
{
    if ( input_bits_left == 0 )
    {
        current_byte++;
        input_bytes_left--;
        input_bits_left = 8;
        if ( input_bytes_left == 0 )
        {
            input_bytes_left = fread( buffer, 1, BUFFER_SIZE, stream );
            if ( input_bytes_left == 0 )
            {
                if ( past_eof )
                {
                    fprintf( stderr, "Bad input file\n" );
                    exit( -1 );
                }
                else
                {
                    past_eof = 1;
                    input_bytes_left = 2;
                }
            }
            current_byte = buffer;
        }
    }
    input_bits_left--;
    return ( ( *current_byte >> input_bits_left ) & 1 );
}

long bit_ftell_output( FILE *stream )
{
    long total;

    total = ftell( stream );
    total += current_byte - buffer;
    total += underflow_bits/8;
    return( total );
}

long bit_ftell_input( FILE *stream )
{
    return( ftell( stream ) - input_bytes_left + 1 );
}


void initialize_arithmetic_encoder()
{
    low = 0;
    high = 0xffff;
    underflow_bits = 0;
}

void encode_symbol( FILE *stream, SYMBOL *s )
{
    long range;
/*
 * These three lines rescale high and low for the new symbol.
 */
    range = (long) ( high-low ) + 1;
    high = low + (unsigned short int )
                 (( range * s->high_count ) / s->scale - 1 );
    low = low + (unsigned short int )
                 (( range * s->low_count ) / s->scale );
/*
 * This loop turns out new bits until high and low are far enough
 * apart to have stabilized.
 */
    for ( ; ; )
    {
/*
 * If this test passes, it means that the MSDigits match, and can
 * be sent to the output stream.
 */
        if ( ( high & 0x8000 ) == ( low & 0x8000 ) )
        {
            output_bit( stream, high & 0x8000 );
            while ( underflow_bits > 0 )
            {
                output_bit( stream, ~high & 0x8000 );
                underflow_bits--;
            }
        }
/*
 * If this test passes, the numbers are in danger of underflow, because
 * the MSDigits don't match, and the 2nd digits are just one apart.
 */
        else if ( ( low & 0x4000 ) && !( high & 0x4000 ))
        {
            underflow_bits += 1;
            low &= 0x3fff;
            high |= 0x4000;
        }
        else
            return ;
        low <<= 1;
        high <<= 1;
        high |= 1;
    }
}

void flush_arithmetic_encoder( FILE *stream )
{
    output_bit( stream, low & 0x4000 );
    underflow_bits++;
    while ( underflow_bits-- > 0 )
        output_bit( stream, ~low & 0x4000 );
}

short int get_current_count( SYMBOL *s )
{
    long range;
    short int count;

    range = (long) ( high - low ) + 1;

    count = (short int)
            ((((long) ( code - low ) + 1 ) * s->scale-1 ) / range );

    return( count );
}

void initialize_arithmetic_decoder( FILE *stream )
{
    int i;

    code = 0;
    for ( i = 0 ; i < 16 ; i++ )
    {
        code <<= 1;
        code += input_bit( stream );
    }
    low = 0;
    high = 0xffff;
}

void remove_symbol_from_stream( FILE *stream, SYMBOL *s )
{
    long range;

/*
 * First, the range is expanded to account for the symbol removal.
 */
    range = (long)( high - low ) + 1;
    high = low + (unsigned short int)
                 (( range * s->high_count ) / s->scale - 1 );
    low = low + (unsigned short int)
                 (( range * s->low_count ) / s->scale );
/*
 * Next, any possible bits are shipped out.
 */
    for ( ; ; )
    {
/*
 * If the MSDigits match, the bits will be shifted out.
 */
        if ( ( high & 0x8000 ) == ( low & 0x8000 ) )
        {
        }
/*
 * Else, if underflow is threatining, shift out the 2nd MSDigit.
 */
        else if ((low & 0x4000) == 0x4000  && (high & 0x4000) == 0 )
        {
            code ^= 0x4000;
            low   &= 0x3fff;
            high  |= 0x4000;
        }
/*
 * Otherwise, nothing can be shifted out, so I return.
 */
        else
            return;
        low <<= 1;
        high <<= 1;
        high |= 1;
        code <<= 1;
        code += input_bit( stream );
    }
}



void compress( void );
void expand( void );
void convert_int_to_symbol( int c, SYMBOL *s );
int convert_symbol_to_int( unsigned int count, SYMBOL *s );
void error_exit( char *message );

void init_frecs()
{
    int         i;
    for (i = 0; i < 256; i++) frec[i]=0;
    frec[256]=1;
}

void print_ppmc();

int main()
{

    time_t      t;
    time_t      start_t;
    time_t      end_t;

    start_t = time(&t);

    altas_count = 0;
    ppmc_count = 0;
    ctx_count  = 0;
    ppmc_altas = (struct ppmc_line*)calloc(altas_size, struct_line_size );
    ppmc_models = (struct ppmc_line*)calloc(ppmc_size, struct_line_size );
    if( !ppmc_altas ) exit(1);
    if( !ppmc_models) exit(1);

    addline_counter = 0;
    compress();

    if( ppmc_altas ) free( ppmc_altas );
    if( ppmc_models ) free( ppmc_models );

    end_t = time(&t);
    printf("\n%lu segundos en comprimir\n", end_t - start_t);


    start_t = time(&t);

    altas_count = 0;
    ppmc_count = 0;
    ctx_count  = 0;
    ppmc_altas = (struct ppmc_line*)calloc(altas_size, struct_line_size );
    ppmc_models = (struct ppmc_line*)calloc(ppmc_size, struct_line_size );
    if( !ppmc_altas ) exit(1);
    if( !ppmc_models) exit(1);

    addline_counter = 0;
    expand();

    if( ppmc_altas ) free( ppmc_altas );
    if( ppmc_models ) free( ppmc_models );


    end_t = time(&t);

    printf("\n%lu segundos en descomprimir\n", end_t - start_t);

    printf("\nmemoria usada: %dKb\n", altas_count*struct_line_size /1024);
    return 0;
}


static int cmp_ppmc_line_sort(const void *p1, const void *p2) {
    return memcmp(p1,p2,7);
}

void print_ppmc_line(struct ppmc_line * ppmc_line) {
    printf("%d.%c%c%c%c%c.%c.%d\n",ppmc_line->model,
                                   ppmc_line->context[0],
                                   ppmc_line->context[1],
                                   ppmc_line->context[2],
                                   ppmc_line->context[3],
                                   ppmc_line->context[4],
                                   ppmc_line->symbol,
                                   ppmc_line->frec);
}

void print_ppmc()
{
    int i;
    printf("models\n");
    for (i = 0; i < ppmc_count; i++) 
        print_ppmc_line( &ppmc_models[i] );
    printf("\n");
    printf("altas\n");
    for (i = 0; i < altas_count; i++) 
        print_ppmc_line( &ppmc_altas[i] );
    
}

static int cmp_ppmc_model_ctx(const void *p1, const void *p2) {
    //printf("searching\n");
    return memcmp(p1,p2,6);
}

void ppmc_sort()
{
    qsort(ppmc_altas, altas_count, sizeof(struct ppmc_line),
            cmp_ppmc_line_sort);
}

struct ppmc_line * ppmc_find( struct ppmc_line * key )
{
    return bsearch(key, ppmc_altas, altas_count,
                          //sizeof(struct ppmc_line), cmp_ppmc_line_find);
                          sizeof(struct ppmc_line), cmp_ppmc_line_sort);
}

struct ppmc_line * ppmc_model_find( struct ppmc_line * area, int area_count, struct ppmc_line * key )
{
    return bsearch(key, area, area_count,
                          //sizeof(struct ppmc_line), cmp_ppmc_line_find);
                          sizeof(struct ppmc_line), cmp_ppmc_model_ctx);
}

void merge()
{
    int ppmc_count_original = ppmc_count;
    ppmc_count--; // Si lo que me pasan es la cantidad, resto 1 para que me queden como indices.
    altas_count--;

    while(ppmc_count>=0 && altas_count>=0)
        if (memcmp(&ppmc_altas[altas_count],&ppmc_models[ppmc_count],7)>=0) {
            memcpy(&ppmc_models[altas_count+ppmc_count+1],&ppmc_altas[altas_count], struct_line_size);
            altas_count--;
            ppmc_count_original++;
        }
        else {
            memcpy(&ppmc_models[altas_count+ppmc_count+1],&ppmc_models[ppmc_count], struct_line_size);
            ppmc_count--;
        }
    if(altas_count>=0)
        for( ;altas_count>=0;altas_count--) {
            memcpy(&ppmc_models[altas_count],&ppmc_altas[altas_count], struct_line_size);
            ppmc_count_original++;
        }

    ppmc_count = ppmc_count_original;
    altas_count++;
}

int ppmc_add_line( struct ppmc_line * line )
{
    //print_ppmc_line( line );
    addline_counter++;
    if (addline_counter == 10) {
        //getchar();
    }
    if ((ppmc_count+altas_count)<ppmc_size)
    {
        memcpy(&ppmc_altas[altas_count], line, struct_line_size);
        altas_count++;
        ppmc_sort();

        if(altas_count>=altas_size)
            merge();

        return 1;
    }
    return 0;
}

/*
int ppmc_add_line( struct ppmc_line * line )
{
    int                 j;
    int                 i;
    struct ppmc_line *  ptr_ppmc_line;

    i = altas_count;

    //if altas_count > ppmc_size return 1;
    ptr_ppmc_line = &ppmc_altas[i];

    ptr_ppmc_line->model = line->model;
    ptr_ppmc_line->symbol = line->symbol;
    for (j = 0; j < ctx_max; j++) {
            ptr_ppmc_line->context[j] = line->context[j];
    }
    ptr_ppmc_line->age = line->age;
    ptr_ppmc_line->frec = line->frec;

    altas_count++;
    ppmc_sort();
    return 0;
}

  */

void ppmc_get_context_start_and_len(struct ppmc_line * area,int area_count, int ctx_index, int * ctx_start, int * ctx_len)
{
    int         j;

    //busco para arriba
    j = ctx_index;
    while( (j-1)>=0 && cmp_ppmc_model_ctx(&area[j-1], &area[ctx_index])==0 ) j--;
    *ctx_start = j;

    //busco para abajo
    j = ctx_index;
    while( (j+1) < area_count && cmp_ppmc_model_ctx(&area[j+1], &area[ctx_index])==0 ) j++;
    *ctx_len = j - *ctx_start;

}

void error_exit( char *message )
{
    puts( message );
    exit( -1 );
}


void clear_frecs()
{
    int         i;

    for (i = 0; i < 257; i++) frec[i]=0;
}

void print_frecs()
{
    int         i;
    int         fr;

    for (i = 0; i < 257; i++) {
        if( (i%20)==0) printf("\n");
        fr = frec[i];
        if ( fr == 0 ) {
            printf(". ");
        } else {
            printf("%d ", fr);
        }
    }
    printf("\n\n");
}

void print_frecs2()
{
    int         i;
    int         fr;

    for (i = 0; i < 257; i++) {
        if( (i%20)==0) printf("\n");
        fr = frec[i];
        if ( fr == 0 ) {
            printf(". ");
        } else {
            printf("%c ", i);
        }
    }
    printf("\n\n");
}
int sum_frecs()
{
    int                  i;
    unsigned int         sum;

    sum = 0;
    for (i = 0; i < 257; i++)
        sum += frec[i];

    return sum;
}

void renorm_frecs()
{
    /* chequeo que la escala no me haga overflow...
     * si hizo overflow, tengo que reescalar todos los numeros */

    int                  i;
    unsigned int         sum;
    unsigned int         tmp;

    sum = 0;
    for (i = 0; i < 257; i++)
        sum += frec[i];
    while (sum >= MAXIMUM_SCALE) {
        sum = 0;
        for (i = 0; i < 257; i++) {
            tmp = frec[i]>>1;
            if (tmp == 0 && frec[i]) tmp = 1;
            frec[i]=tmp;
            sum += frec[i];
        }
    }

}


int ppm_has_symbols()
{
    return !ctx_found;
}

void ppm_preparar_para_buscar()
{
    int                 z;

    //INICIALIZO PARA EL QUE VOY A BUSCAR
    ppmline.model  = ctx_count;
    memcpy(&ppmline.context[0], &ctx[0],5);
    j = ctx_count;
    ctx_found = 0;
    for (z = 0; z < 257; z++) exclusion[z] = 1;
}


int ppm_step_buscar()
{
    int                 z;
    ptrdiff_t           ctx_index;
    char                model_symbol;
    int                 model_frec;
    int                 escape_frec;
    int                 k;

    ppm_find_altas = NULL;
    ppm_find_modelos = NULL;
    clear_frecs();

    out_symbol     = (unsigned int)ESCAPE;
    frec[ ESCAPE ] = 1;

    if (j==-1) {
        frec[ ESCAPE ] = 1;
        for (z = 0; z < 256; z++) frec[ z ] = exclusion[ z ];
        ctx_found       = 1;
        return 0;
    }

    //BUSCO ALGO DEL MODELO EN ALTAS
    ppm_find_altas = ppmc_model_find( ppmc_altas, altas_count, &ppmline );
    ppm_find_modelos = ppmc_model_find( ppmc_models, ppmc_count, &ppmline );

    if (ppm_find_altas) {

        ctx_found = 1;
        ctx_index = ppm_find_altas - &ppmc_altas[0];
        ppmc_get_context_start_and_len(ppmc_altas, altas_count, ctx_index, &ctx_start_altas, &ctx_len_altas);

        escape_frec =  ctx_len_altas + 1;

        for (k = 0; k <= ctx_len_altas; k++) {
            model_symbol = (int)(ppmc_altas[ctx_start_altas + k].symbol);
            model_frec   = ppmc_altas[ctx_start_altas + k].frec;
            frec[ (unsigned char)model_symbol ]  = model_frec*exclusion[ (unsigned char)model_symbol ];
        }

        frec[ ESCAPE ] = escape_frec;
    }

    if (ppm_find_modelos) {

        ctx_found = 1;
        ctx_index = ppm_find_modelos - &ppmc_models[0];
        ppmc_get_context_start_and_len(ppmc_models, ppmc_count, ctx_index, &ctx_start_models, &ctx_len_models);
        escape_frec =  ctx_len_models + 1;

        for (k = 0; k <= ctx_len_models; k++) {
            model_symbol = (int)(ppmc_models[ctx_start_models + k].symbol);
            model_frec   = ppmc_models[ctx_start_models + k].frec;
            frec[ (unsigned char)model_symbol ]  = model_frec*exclusion[ (unsigned char)model_symbol ];
        }


        if (ppm_find_altas) 
            frec[ ESCAPE ] += escape_frec;
        else
            frec[ ESCAPE ] = escape_frec;
    }


    if (ppm_find_altas) 
        for (k = 0; k <= ctx_len_altas; k++) 
            exclusion[ (unsigned char)(ppmc_altas[ctx_start_altas + k].symbol) ] = 0;

    if (ppm_find_modelos) 
        for (k = 0; k <= ctx_len_models; k++) 
            exclusion[ (unsigned char)(ppmc_models[ctx_start_models + k].symbol) ] = 0;


    if (ppm_find_altas || ppm_find_modelos) 
        renorm_frecs();
    //printf("%d\n", frec[ESCAPE]);
    //print_frecs();
    //print_ppmc();
    return 0;
}

void ppm_step_actualizar()
{
    char                model_symbol;
    int                 k;
    char                cur_char;
    struct ppmc_line *  ppmline_actualizar;
    int                 found_here;

    found_here = 0;

    cur_char = cur_char_g;

    if (j!=-1) {
        if (ppm_find_altas) {
            for (k = 0; k <= ctx_len_altas; k++) {
                model_symbol = (int)(ppmc_altas[ctx_start_altas + k].symbol);
                //ACTUALIZO
                if ( model_symbol == cur_char) {
                    ppmline_actualizar = &ppmc_altas[ctx_start_altas + k];
                    ctx_found = 1;
                    found_here = 1;
                }
            }

            if ( ctx_found && found_here ) {
                //ACTUALIZO LA FRECUENCIA
               //if ( ppmline_actualizar ) {
                   ppmline_actualizar->frec++;
                   found_here = 0;
                   ppmline_actualizar = NULL;
                    //print_ppmc_line( ppmline_actualizar );
               //}
            }
        }

        if (ppm_find_modelos) {
            for (k = 0; k <= ctx_len_models; k++) {
                model_symbol = (int)(ppmc_models[ctx_start_models + k].symbol);
                //ACTUALIZO
                if ( model_symbol == cur_char) {
                    ppmline_actualizar = &ppmc_models[ctx_start_models + k];
                    ctx_found = 1;
                    found_here = 1;
                }
            }

            if ( ctx_found && found_here ) {
                //ACTUALIZO LA FRECUENCIA
               //if ( ppmline_actualizar ) 
                   ppmline_actualizar->frec++;
            }
        }
        //ACTUALIZO

        if (!ctx_found) {
            ppmline.symbol=cur_char;
            //printf("%c\n", cur_char);
            //print_ppmc_line( &ppmline ); 
            ppmc_add_line( &ppmline );
        }
    }


    ppmline.context[0] = ppmline.context[1];
    ppmline.context[1] = ppmline.context[2];
    ppmline.context[2] = ppmline.context[3];
    ppmline.context[3] = ppmline.context[4];
    ppmline.context[4] = '\0';
    ppmline.model--;
    j--;


}

void ppm_shiftear_contexto()
{
    //ACTUALIZO PARA EL PROXIMO CARACTER
    if (ctx_count < ctx_max) {
        ctx[ ctx_count ] = cur_char_g;
        ctx_count++;
    } else {
        ctx[0] = ctx[1];
        ctx[1] = ctx[2];
        ctx[2] = ctx[3];
        ctx[3] = ctx[4];
        ctx[4] = cur_char_g;
    }
}


unsigned short int ppm_get_next_symbol( char cur_char )
{

    cur_char_g = cur_char;

    if ( frec[ (unsigned char ) cur_char ]  ){
        return (unsigned char) cur_char;}
    else {
        ctx_found = 0;
        return ESCAPE;
    }
}


void compress()
{
    int                     i;
    unsigned short int      c;
    SYMBOL                  s;
    FILE *                  plaintext_file;
    static unsigned char    read_buffer[ BUFFER_SIZE + 2 ];
    int                     read_bytes;
    FILE *                  compressed_file;

    struct stat             st;
    unsigned int            size;
    unsigned int            actualpos;
    int                     porcentaje;
    static char             bar[BARRITA_MAX];

    int                     ki;

    for( ki = 0 ; ki < BARRITA_MAX ; ki++ ) bar[ ki ] = BARRITA_FALTA;

    bar[ BARRITA_MAX ] = '\0';

    porcentaje = 0;
    actualpos = 0;
    plaintext_file=fopen( "test.txt", "rb" );
    if ( plaintext_file == NULL )
        error_exit( "Could not open input file" );

    stat("test.txt", &st);
    size = st.st_size;

    compressed_file=fopen( "test.cmp", "wb" );

    if ( compressed_file == NULL )
        error_exit( "Could not open output file" );

    puts( "Compressing..." );
    initialize_output_bitstream();
    initialize_arithmetic_encoder();

    ctx_count = 0;
    //Inicializacion de Variables
    ppmline.frec = 1;
    ppmline.age = '\1';
    for (i = 0; i <= ctx_max; i++) ctx[i] = '\0';

    while( (read_bytes = fread( read_buffer, 1, BUFFER_SIZE, plaintext_file ) ) > 0 ) {
        for (i = 0; i < read_bytes; i++) {

            actualpos++;
            porcentaje     = (int)(((float)actualpos/(float)size)*BARRITA_MAX);

            if (bar[porcentaje] != BARRITA_LISTO)
            {
            	bar[porcentaje] = BARRITA_LISTO;
            	printf("\r[%s]", bar);
            	printf(" | %d %% |",porcentaje);
                fflush(stdout);

            }

            c = read_buffer[i];
            cur_char = read_buffer[i];

            //PPMC PEDIR MODELO
            ppm_preparar_para_buscar();

            while( ppm_has_symbols() ) {
                //BUSCO EL ARRAY
                ppm_step_buscar();
                //print_frecs();
                out_symbol = ppm_get_next_symbol( cur_char );

        //printf("addline_counter %d\n", addline_counter);
        //print_frecs();
                convert_int_to_symbol( out_symbol, &s );
                encode_symbol( compressed_file, &s );
                //LLAMO AL ARITMETICO CON EL ARRAY DE FRECUENCIAS Y EL SIMBOLO
                //(out_symbol == ESCAPE)?printf("ESC"):printf("%c",out_symbol);
                //printf("(%d/%d)", frec[out_symbol],sum_frecs());

                ppm_step_actualizar();
            }
            ppm_shiftear_contexto();
        }
    }

    if( feof( plaintext_file ) ) {
        //c = ESCAPE;
        //out_symbol=ESCAPE;

        ppm_preparar_para_buscar();
        int zz;
        for (zz = 0; zz < 7; zz++) {//ARREGLO CABEZA FUERRRRTE //while( ppm_has_symbols() ) {
            //BUSCO EL ARRAY

            ppm_step_buscar();
            //print_frecs();


            ppm_get_next_symbol( c );
            out_symbol=ESCAPE;

            //convert_int_to_symbol( c, &s );
            convert_int_to_symbol( out_symbol, &s );
            encode_symbol( compressed_file, &s );

            //(out_symbol == ESCAPE)?printf("ESC"):printf("%c",out_symbol);
            //printf("(%d/%d)", frec[out_symbol],sum_frecs());
            //LLAMO AL ARITMETICO CON EL ARRAY DE FRECUENCIAS Y EL SIMBOLO

            ppm_step_actualizar();
            if(j==-2) break;
        }
    }
    flush_arithmetic_encoder( compressed_file );
    flush_output_bitstream( compressed_file );
    fclose( compressed_file );
    fclose( plaintext_file );

}

void expand()
{
    FILE *                  compressed_file;
    FILE *                  decompressed_file;
    SYMBOL                  s;
    unsigned short int      c;
    int                     count;
    static unsigned char    write_buffer[ BUFFER_SIZE + 2 ];
    int                     i;
    int                     ki;

    struct stat             st;
    unsigned int            size;
    unsigned int            actualpos;
    unsigned int            decoded_count;
    int                     porcentaje;
    static char             bar[BARRITA_MAX];

    //Inicializacion de Variables
    for( ki = 0 ; ki < BARRITA_MAX ; ki++ ) bar[ ki ] = BARRITA_FALTA;
    bar[ BARRITA_MAX ] = '\0';
    stat("test.cmp", &st);
    size = st.st_size;
    porcentaje = 0;
    actualpos = 0;
    decoded_count = 0;
    ctx_count = 0;
    ppmline.frec = 1;
    ppmline.age = '\1';
    for (i = 0; i <= ctx_max; i++) ctx[i] = '\0';

    compressed_file=fopen( "test.cmp", "rb" );
    if ( compressed_file == NULL )
        error_exit( "Could not open output file" );

    decompressed_file=fopen( "test.dcp", "wb" );
    if ( decompressed_file == NULL )
        error_exit( "Could not open decompression file" );
    puts( "Decoding..." );
    //printf( "Incoming characters: " );
    initialize_input_bitstream();
    initialize_arithmetic_decoder( compressed_file );

    init_frecs();
    j = -1;

    for(ki=0;ki<256;ki++) exclusion[ki]=1; exclusion[256]=1;
    int r = 0;


    ppm_preparar_para_buscar();
    for ( ; ; ) {
        
        decoded_count++;
        if (decoded_count > 512) {
            decoded_count = 0;

            actualpos = ftell( compressed_file );

            porcentaje     = (int)(((float)actualpos/(float)size)*BARRITA_MAX);
            for( ki = 0 ; ki < porcentaje ; ki++ ) bar[ ki ] = BARRITA_LISTO;

            if (bar[porcentaje] != BARRITA_LISTO)
            {
                bar[porcentaje] = BARRITA_LISTO;
                printf("\r[%s]", bar);
                printf(" | %d %% |",porcentaje);
                fflush(stdout);
            }

        }
        //BUSCO EL ARRAY
        ppm_step_buscar();
        //print_frecs();
        //LLAMO AL ARITMETICO CON EL ARRAY DE FRECUENCIAS Y EL SIMBOLO
        s.scale = sum_frecs();

        count = get_current_count( &s );
        c = convert_symbol_to_int( count, &s );
        out_symbol=ppm_get_next_symbol(c);
        ppmline.symbol=out_symbol;
        cur_char=out_symbol;

	//(c == ESCAPE)?printf("ESC"):printf("(%c)",out_symbol);printf("(%d/%d)", frec[c],sum_frecs());
        remove_symbol_from_stream( compressed_file, &s );

        if (c < 256) {
            ppm_step_actualizar();
            int aux=j+1;
            ppm_preparar_para_buscar();
            ctx_found=0;

            while(j>aux){

                //print_frecs();
                //(c == ESCAPE)?printf("ESC"):printf("(%c)",c);printf("(%d/%d)", frec[c],sum_frecs());
                //printf("\n");

                ppmline.symbol = c;
                ppmline.frec   = 1;
                cur_char_g = c;
                cur_char = c;
                ppm_find_altas = NULL;
                ppm_find_modelos = NULL;

                ppm_step_actualizar();

            }
            write_buffer[0] = cur_char;
            fwrite( write_buffer, 1, 1, decompressed_file );

            ppm_shiftear_contexto();
            ppm_preparar_para_buscar();

        } else {
            if (j==-1 ){
                break;
            }
                ppmline.context[0] = ppmline.context[1];
                ppmline.context[1] = ppmline.context[2];
                ppmline.context[2] = ppmline.context[3];
                ppmline.context[3] = ppmline.context[4];
                ppmline.context[4] = '\0';
                ppmline.model--;
                j--;
        }
        //printf("\n");



    }
    fclose( decompressed_file );
}
void convert_int_to_symbol( int c, SYMBOL *s )
{
    int     i;
    int     low;
    int     high;

    low = 0;
    high = 0;

    for (i = 0; i < 257; i++) {
        high = low + frec[i];
        if ( c == i ) {
            s->low_count = low;
            s->high_count = high;
        }
        low = high;
    }
    s->scale = high;
    return;
}

int convert_symbol_to_int( unsigned int count, SYMBOL *s )
{
    int     i;
    int     low;
    int     high;
    int     result;

    low = 0;
    high = 0;
    result = 0;

    for (i = 0; i < 257; i++) {
        high = low + frec[i];
        if ( count >= low && count < high ) {
            s->low_count = low;
            s->high_count = high;
            result = i;
        }
        low = high;
    }
    s->scale = high;
    return( result );
}

